/*
	Base64 system based on code from b64 project (http://base64.sourceforge.net/b64.c).
*/
#include "CryptoSystem.h"
#include "Config.h"
#include "AES.h"
#include "SHA512.h"

#include <cstdlib>
#include <time.h>

CryptoSystem::CryptoSystem()
{
}

CryptoSystem::~CryptoSystem()
{
}

void CryptoSystem::Init()
{
	std::string key = conf.GetValue("network.crypto.keyfile");
	FILE* file = fopen(key.c_str(), "r");
	memset(Key, 0, 16);
	if (!file)
	{
		fprintf(stderr, "CryptoSystem.Init: Could not open key file `%s'\n", key.c_str());
		return;
	}
	fread(Key, 16, 1, file);
	fclose(file);
}

void CryptoSystem::Encrypt(NetworkMessage& msg)
{
	static unsigned char BaseIV[16] = {
		0, 144, 49, 207,
		149, 122, 89, 229,
		210, 191, 44, 219,
		181, 131, 77, 3
	};
	unsigned char IV[16];
	unsigned char* buffer;
	unsigned char block[16];
	int temp = time(NULL) & 0xFF;
	int i = 0, j = 0;
	for (i = 0; i < 16; i++)
	{
		block[i] = (IV[i] & temp) + temp;
	}
	StateAES state;
	i = msg.GetLength();
	i = i + (16 - (i % 16));
	buffer = new unsigned char[i];
	memcpy(buffer, msg.GetBodyBuffer(), msg.GetLength());
	memset(buffer + msg.GetLength(), 0, i - msg.GetLength());
	msg.Reset();
	temp = 0; // offset inside the bufer
	msg.AddRaw((const char*)IV, 16);
	while (temp < i)
	{
		AES_Init(&state, Key);
		AES_Encrypt(&state, IV);
		AES_Final(&state, IV);
		for (j = 0; j < 16; j++)
		{
			block[j] = buffer[temp + j] ^ IV[j];
		}
		msg.AddRaw((const char*)block, 16);
		temp += 16;
	}
}

void CryptoSystem::Decrypt(NetworkMessage& msg) // does not work
{
	unsigned char IV[16];
	unsigned char* buffer;
	unsigned char block[16];
	int temp = time(NULL) & 0xFF;
	int i = 0, j = 0;
	StateAES state;
	memcpy(IV, msg.GetBodyBuffer(), 16);
	i = msg.GetLength() - 16;
	buffer = new unsigned char[i];
	memcpy(buffer, msg.GetBodyBuffer() + 16, i);
	msg.Reset();
	temp = 0; // offset inside the bufer
	while (temp < i)
	{
		AES_Init(&state, Key);
		AES_Encrypt(&state, IV);
		AES_Final(&state, IV);
		for (j = 0; j < 16; j++)
		{
			block[j] = buffer[temp + j] ^ IV[j];
		}
		msg.AddRaw((const char*)block, 16);
		temp += 16;
	}
}

std::string CryptoSystem::Hash(std::string str)
{
	static const char* hex = "0123456789abcdef";
	unsigned char* bytes = (unsigned char*)str.c_str();
	unsigned char digest[64];
	int len = str.length();
	std::string ret;
	StateSHA512 state;
	SHA512_Init(&state);
	SHA512_Update(&state, (const char*)bytes, len);
	SHA512_Final(&state, digest);
	for (int i = 0; i < 64; i++)
	{
		ret += (hex[(digest[i] >> 4) & 0x0F]);
		ret += (hex[(digest[i]) & 0x0F]);
	}
	return ret;
}

CryptoSystem& CryptoSystem::Instance()
{
	static CryptoSystem _instance;
	return _instance;
}
